package com.bstek.urule.console.servlet.frame;

import com.bstek.urule.RuleException;
import com.bstek.urule.Utils;
import com.bstek.urule.console.EnvironmentUtils;
import com.bstek.urule.console.User;
import com.bstek.urule.console.repository.Repository;
import com.bstek.urule.console.repository.RepositoryService;
import com.bstek.urule.console.repository.model.FileType;
import com.bstek.urule.console.repository.model.RepositoryFile;
import com.bstek.urule.console.repository.model.Type;
import com.bstek.urule.console.repository.model.VersionFile;
import com.bstek.urule.console.servlet.RequestContext;
import com.bstek.urule.console.servlet.base.BaseController;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 首页
 * @author:gehongbin
 * @version 1.0
 * @date 2021/12/08
 */
@Controller
public class FrameController extends BaseController {

	private static final String CLASSIFY_COOKIE_NAME="_lib_classify";

	@Autowired
	private RepositoryService repositoryService;

	/**
	 * 首页页面
	 * @param req
	 * @param resp
	 * @return
	 */
	@GetMapping("/urule/frame")
	public ModelAndView frame(HttpServletRequest req, HttpServletResponse resp) {
		ModelAndView mv = new ModelAndView("/frame");
		mv.addObject("contextPath", req.getContextPath());
		mv.addObject("welcomePage", "");
		mv.addObject("title", "URule Console");
		return mv;
	}

	/**
	 * 获取项目列表
	 * @param req
	 * @param resp
	 * @throws Exception
	 */
	@PostMapping(value = "/urule/frame/loadProjects")
	public void loadProjects(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		User user= EnvironmentUtils.getLoginUser(new RequestContext(req,resp));
		boolean classify = getClassify(req,resp);
		String projectName=req.getParameter("projectName");
		String searchFileName=req.getParameter("searchFileName");
		projectName= Utils.decodeURL(projectName);
		String typesStr=req.getParameter("types");
		FileType[] types=null;
		if(StringUtils.isNotBlank(typesStr) && !typesStr.equals("all")){
			if(typesStr.equals("lib")){
				types=new FileType[]{FileType.VariableLibrary,FileType.ConstantLibrary,FileType.ParameterLibrary,FileType.ActionLibrary};
			}else if(typesStr.equals("rule")){
				types=new FileType[]{FileType.Ruleset,FileType.UL};
			}else if(typesStr.equals("table")){
				types=new FileType[]{FileType.DecisionTable,FileType.ScriptDecisionTable};
			}else if(typesStr.equals("tree")){
				types=new FileType[]{FileType.DecisionTree};
			}else if(typesStr.equals("flow")){
				types=new FileType[]{FileType.RuleFlow};
			}
		}
		Repository repo=repositoryService.loadRepository(projectName, user,classify,types,searchFileName);
		Map<String,Object> map=new HashMap<String,Object>();
		map.put("repo", repo);
		map.put("classify", classify);
		writeObjectToJson(resp, map);
	}


	@ResponseBody
	@PostMapping(value = "/urule/frame/fileVersions")
	public List<VersionFile> fileVersions(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		String path=req.getParameter("path");
		path=Utils.decodeURL(path);
		return repositoryService.getVersionFiles(path);
	}

	@PostMapping(value = "/urule/frame/fileSource")
	public void fileSource(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		String path=req.getParameter("path");
		path=Utils.decodeURL(path);
		InputStream inputStream=repositoryService.readFile(path,null);
		String content= IOUtils.toString(inputStream,"utf-8");
		inputStream.close();
		String xml=null;
		try{
			Document doc= DocumentHelper.parseText(content);
			OutputFormat format=OutputFormat.createPrettyPrint();
			StringWriter out=new StringWriter();
			XMLWriter writer=new XMLWriter(out, format);
			writer.write(doc);
			xml=out.toString();
		}catch(Exception ex){
			xml=content;
		}
		Map<String,Object> result=new HashMap<String,Object>();
		result.put("content", xml);
		writeObjectToJson(resp, xml);
	}

	@PostMapping(value = "/urule/frame/importProject")
	@ResponseBody
	public void importProject(@RequestParam(name = "overwriteProject",required = false) String overwriteProject,
							  @RequestParam(name = "file",required = false) MultipartFile file,
							  HttpServletRequest req, HttpServletResponse resp) throws Exception {
		if(file == null){
			throw new ServletException("Upload file is invalid.");
		}
		Boolean overwrite = Boolean.valueOf(overwriteProject);
		InputStream inputStream = file.getInputStream();
		repositoryService.importXml(inputStream,overwrite);
		IOUtils.closeQuietly(inputStream);
		resp.sendRedirect(req.getContextPath()+"/urule/frame");
	}

	@ResponseBody
	@PostMapping(value = "/urule/frame/loadFileVersions")
	public List<VersionFile> loadFileVersions(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		String file=req.getParameter("file");
		file=Utils.decodeURL(file);
		return repositoryService.getVersionFiles(file);
	}

	@PostMapping(value = "/urule/frame/createFolder")
	public void createFolder(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		String fullFolderName=req.getParameter("fullFolderName");
		fullFolderName=Utils.decodeURL(fullFolderName);
		User user=EnvironmentUtils.getLoginUser(new RequestContext(req,resp));
		repositoryService.createDir(fullFolderName, user);
		loadProjects(req, resp);
	}

	@PostMapping(value = "/urule/frame/copyFile")
	public void copyFile(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String newFullPath=req.getParameter("newFullPath");
		String oldFullPath=req.getParameter("oldFullPath");
		newFullPath=Utils.decodeURL(newFullPath);
		oldFullPath=Utils.decodeURL(oldFullPath);
		try{
			InputStream inputStream=repositoryService.readFile(oldFullPath, null);
			String content=IOUtils.toString(inputStream, "utf-8");
			inputStream.close();
			User user=EnvironmentUtils.getLoginUser(new RequestContext(req,resp));
			repositoryService.createFile(newFullPath, content,user);
			loadProjects(req, resp);
		}catch(Exception ex){
			throw new RuleException(ex);
		}
	}

	@PostMapping(value = "/urule/frame/createFile")
	public RepositoryFile createFile(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String path=req.getParameter("path");
		path=Utils.decodeURL(path);
		String type=req.getParameter("type");
		FileType fileType=FileType.parse(type);
		StringBuilder content=new StringBuilder();
		if(fileType.equals(FileType.UL)) {
			content.append("rule \"rule01\"");
			content.append("\n");
			content.append("if");
			content.append("\r\n");
			content.append("then");
			content.append("\r\n");
			content.append("end");
		}else if(fileType.equals(FileType.DecisionTable)) {
			content.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
			content.append("<decision-table>");
			content.append("<cell row=\"0\" col=\"2\" rowspan=\"1\"></cell>");
			content.append("<cell row=\"0\" col=\"1\" rowspan=\"1\">");
			content.append("<joint type=\"and\"/>");
			content.append("</cell>");
			content.append("<cell row=\"0\" col=\"0\" rowspan=\"1\">");
			content.append("<joint type=\"and\"/>");
			content.append("</cell>");
			content.append("<cell row=\"1\" col=\"2\" rowspan=\"1\">");
			content.append("</cell>");
			content.append("<cell row=\"1\" col=\"1\" rowspan=\"1\">");
			content.append("<joint type=\"and\"/>");
			content.append("</cell>");
			content.append("<cell row=\"1\" col=\"0\" rowspan=\"1\">");
			content.append("<joint type=\"and\"/>");
			content.append("</cell>");
			content.append("<row num=\"0\" height=\"40\"/>");
			content.append("<row num=\"1\" height=\"40\"/>");
			content.append("<col num=\"0\" width=\"120\" type=\"Criteria\"/>");
			content.append("<col num=\"1\" width=\"120\" type=\"Criteria\"/>");
			content.append("<col num=\"2\" width=\"200\" type=\"Assignment\"/>");
			content.append("</decision-table>");
		}else if(fileType.equals(FileType.DecisionTree)){
			content.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
			content.append("<decision-tree>");
			content.append("<variable-tree-node></variable-tree-node>");
			content.append("</decision-tree>");
		}else if(fileType.equals(FileType.ScriptDecisionTable)) {
			content.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
			content.append("<script-decision-table>");
			content.append("<script-cell row=\"0\" col=\"2\" rowspan=\"1\"></script-cell>");
			content.append("<script-cell row=\"0\" col=\"1\" rowspan=\"1\"></script-cell>");
			content.append("<script-cell row=\"0\" col=\"0\" rowspan=\"1\"></script-cell>");
			content.append("<script-cell row=\"1\" col=\"2\" rowspan=\"1\"></script-cell>");
			content.append("<script-cell row=\"1\" col=\"1\" rowspan=\"1\"></script-cell>");
			content.append("<script-cell row=\"1\" col=\"0\" rowspan=\"1\"></script-cell>");
			content.append("<row num=\"0\" height=\"40\"/>");
			content.append("<row num=\"1\" height=\"40\"/>");
			content.append("<col num=\"0\" width=\"120\" type=\"Criteria\"/>");
			content.append("<col num=\"1\" width=\"120\" type=\"Criteria\"/>");
			content.append("<col num=\"2\" width=\"200\" type=\"Assignment\"/>");
			content.append("</script-decision-table>");
		}else if(fileType.equals(FileType.Scorecard)){
			content.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
			content.append("<scorecard scoring-type=\"sum\" assign-target-type=\"none\">");
			content.append("</scorecard>");
		}else{
			String name = getRootTagName(fileType);
			content.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
			content.append("<" + name + ">");
			content.append("</" + name + ">");
		}
		User user=EnvironmentUtils.getLoginUser(new RequestContext(req,resp));
		try{
			repositoryService.createFile(path, content.toString(),user);
		}catch(Exception ex){
			throw new RuleException(ex);
		}
		RepositoryFile newFileInfo=new RepositoryFile();
		newFileInfo.setFullPath(path);
		if(fileType.equals(FileType.VariableLibrary)){
			newFileInfo.setType(Type.variable);
		}else if(fileType.equals(FileType.ActionLibrary)){
			newFileInfo.setType(Type.action);
		}else if(fileType.equals(FileType.ConstantLibrary)){
			newFileInfo.setType(Type.constant);
		}else if(fileType.equals(FileType.ParameterLibrary)){
			newFileInfo.setType(Type.parameter);
		}else if(fileType.equals(FileType.DecisionTable)){
			newFileInfo.setType(Type.decisionTable);
		}else if(fileType.equals(FileType.ScriptDecisionTable)){
			newFileInfo.setType(Type.scriptDecisionTable);
		}else if(fileType.equals(FileType.Ruleset)){
			newFileInfo.setType(Type.rule);
		}else if(fileType.equals(FileType.UL)){
			newFileInfo.setType(Type.ul);
		}else if(fileType.equals(FileType.DecisionTree)){
			newFileInfo.setType(Type.decisionTree);
		}else if(fileType.equals(FileType.RuleFlow)){
			newFileInfo.setType(Type.flow);
		}else if(fileType.equals(FileType.Scorecard)){
			newFileInfo.setType(Type.scorecard);
		}
		return newFileInfo;
	}

	private String getRootTagName(FileType type) {
		String root = null;
		switch(type){
			case ActionLibrary:
				root="action-library";
				break;
			case ConstantLibrary:
				root="constant-library";
				break;
			case DecisionTable:
				root="decision-table";
				break;
			case DecisionTree:
				root="decision-tree";
				break;
			case ParameterLibrary:
				root="parameter-library";
				break;
			case RuleFlow:
				root="rule-flow";
				break;
			case Ruleset:
				root="rule-set";
				break;
			case ScriptDecisionTable:
				root="script-decision-table";
				break;
			case VariableLibrary:
				root="variable-library";
				break;
			case UL:
				root="script";
				break;
			case Scorecard:
				root="scorecard";
				break;
			case DIR:
				throw new IllegalArgumentException("Unsupport filetype : "+type);
		}
		return root;
	}

	@PostMapping(value = "/urule/frame/projectExistCheck")
	public void projectExistCheck(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String projectName=req.getParameter("newProjectName");
		if(StringUtils.isEmpty(projectName)){
			throw new RuleException("newProjectName not null");
		}
		projectName=Utils.decodeURL(projectName);
		projectName=projectName.trim();
		Map<String,Object> result=new HashMap<String,Object>();
		try{
			result.put("valid", !repositoryService.fileExistCheck(projectName));
			writeObjectToJson(resp, result);
		}catch(Exception ex){
			throw new RuleException(ex);
		}
	}

	@PostMapping(value = "/urule/frame/fileExistCheck")
	public void fileExistCheck(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String fullFileName=req.getParameter("fullFileName");
		if(StringUtils.isEmpty(fullFileName)){
			return;
		}
		fullFileName=Utils.decodeURL(fullFileName);
		fullFileName=fullFileName.trim();
		Map<String,Object> result=new HashMap<String,Object>();
		try{
			result.put("valid", !repositoryService.fileExistCheck(fullFileName));
			writeObjectToJson(resp, result);
		}catch(Exception ex){
			throw new RuleException(ex);
		}
	}

	@PostMapping(value = "/urule/frame/deleteFile")
	public void deleteFile(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		String path=req.getParameter("path");
		path=Utils.decodeURL(path);
		User user=EnvironmentUtils.getLoginUser(new RequestContext(req, resp));
		repositoryService.deleteFile(path,user);
		String isFolder=req.getParameter("isFolder");
		if(StringUtils.isNotBlank(isFolder) && isFolder.equals("true")){
			loadProjects(req, resp);
		}
	}

	@PostMapping(value = "/urule/frame/lockFile")
	public void lockFile(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		String file=req.getParameter("file");
		User user=EnvironmentUtils.getLoginUser(new RequestContext(req, resp));
		repositoryService.lockPath(file, user);
		loadProjects(req, resp);
	}

	@PostMapping(value = "/urule/frame/unlockFile")
	public void unlockFile(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		String file=req.getParameter("file");
		User user=EnvironmentUtils.getLoginUser(new RequestContext(req, resp));
		repositoryService.unlockPath(file, user);
		loadProjects(req, resp);
	}

	@GetMapping(value = "/urule/frame/exportProjectBackupFile")
	public void exportProjectBackupFile(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		String path=req.getParameter("path");
		String projectPath=Utils.decodeURL(path);
		if(StringUtils.isEmpty(projectPath)){
			throw new RuleException("Export project not be null.");
		}
		SimpleDateFormat sd=new SimpleDateFormat("yyyyMMddHHmmss");
		String projectName=projectPath.substring(1,projectPath.length());
		String filename=projectName+"-urule-repo-"+sd.format(new Date())+".bak";
		resp.setContentType("application/octet-stream");
		resp.setHeader("Content-Disposition", "attachment; filename=\"" + new String(filename.getBytes("utf-8"),"iso-8859-1") + "\"");
		resp.setHeader("content-type", "application/octet-stream");
		OutputStream outputStream=resp.getOutputStream();
		repositoryService.exportXml(projectPath,outputStream);
		outputStream.flush();
		outputStream.close();
	}

	@PostMapping(value = "/urule/frame/createProject")
	public RepositoryFile createProject(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		String projectName=req.getParameter("newProjectName");
		projectName=Utils.decodeURL(projectName);
		boolean classify = getClassify(req,resp);
		User user=EnvironmentUtils.getLoginUser(new RequestContext(req,resp));
		return repositoryService.createProject(projectName,user,classify);
	}

	@PostMapping(value = "/urule/frame/fileRename")
	public void fileRename(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		String path=req.getParameter("path");
		path=Utils.decodeURL(path);
		String newPath=req.getParameter("newPath");
		newPath=Utils.decodeURL(newPath);
		repositoryService.fileRename(path, newPath);
		loadProjects(req, resp);
	}

	/**
	 * 设置cookie
	 * @param req
	 * @param resp
	 * @return
	 */
	private boolean getClassify(HttpServletRequest req,HttpServletResponse resp) {
		String classifyValue=req.getParameter("classify");
		if(StringUtils.isBlank(classifyValue)){
			Cookie[] cookies=req.getCookies();
			if(cookies!=null){
				for(Cookie cookie:cookies){
					if(CLASSIFY_COOKIE_NAME.equals(cookie.getName())){
						classifyValue=cookie.getValue();
						break;
					}
				}
			}
		}else{
			Cookie classifyCookie=new Cookie(CLASSIFY_COOKIE_NAME,classifyValue);
			classifyCookie.setMaxAge(2100000000);
			resp.addCookie(classifyCookie);
		}
		boolean classify=true;
		if(StringUtils.isNotBlank(classifyValue)){
			classify=Boolean.valueOf(classifyValue);
		}
		return classify;
	}
}
